Small bugfix in the README.
[Graf-Zahl.git] / Graf Zahl.rb
blob1b664f311d9a44f10193eef1322e8fa64b24ec6b
1 #!/usr/bin/env ruby
3 class Character
4   module Moral
5     LAWFUL = 1
6     NEUTRAL = 0.5
7     CHAOTIC = 0
8   end
10   module Ethic
11     GOOD = 1
12     NEUTRAL = 0.5
13     EVIL = 0
14   end
16   TRAITS = ["ethic","moral","tau","i","agility","strength","stamina","expertise"]
17   attr_accessor :traits
18   attr_accessor :traits_delta
20   def initialize(traits = Array.new(TRAITS.size, 0), traits_delta = Array.new(TRAITS.size, 0))
21     #puts "creating character with #{traits.join(',')}"
22     @traits = traits.to_a
23     @traits_delta = traits_delta.to_a
24   end
26   def self.generate(something)
27     ch = doTehArithmancy(something).character
28     #puts "got a number: #{num} chr #{num.instance_variable_get(:@character)}"
29     return ch.clone if ch
30     return Character.new
31   end
33   def acc(chr, factor)
34     return @traits_delta unless chr
35     tplus (tmult (tminus chr.traits, traits), factor), traits_delta
36   end
38   #that stuff is still kinda indirect.
39   def acc!(chr, factor)
40     @traits_delta = acc(chr, factor)
41   end
43   def step! ()
44     @traits = tplus @traits, @traits_delta
45     @traits_delta = tmult @traits_delta, 0.1
46     cap!
47   end
49   def tminus (a,b)
50     a.zip(b).map{|c| c[0]-c[1]}
51   end
53   def tplus (a,b)
54     a.zip(b).map{|c| c[0]+c[1]}
55   end
57   def tmult (a,s)
58     a.map{|t| t*s}
59   end
61   def tdiv (a,s)
62     a.map{|t| t/s}
63   end
65   def length
66     return Math::sqrt(@traits.inject(0){|a,e|a+e*e})
67   end
69   #returns a character
70   def cap! ()
71     @traits = @traits.each_with_index.map do |v, i|
72       next v if ["tau", "i"].include? TRAITS[i]
73       if v < 0
74         0
75       elsif v > 1
76         1
77       else
78         v
79       end
80     end
81   end
83   def traits= (params)
84     @traits = params.zip(@traits).map{|a| a[0] || a[1]}
85   end
87   def to_s
88     TRAITS.zip(@traits).map{|a| a[0]+": "+a[1].to_s }.join ","
89   end
91   def self.doTehArithmancy (something)
92     #puts "dta called: #{something.class}"
93     data = something.to_s.downcase.gsub(/[^a-z0-9]/,'')
94     return 0 unless data.length > 0
95     ary = data.chars.map do |c|
96       case c
97       when '0'..'9' then c.ord-'0'.ord
98       when 'a'..'i' then c.ord-'a'.ord+1
99       when 'j'..'r' then c.ord-'j'.ord+1
100       when 's'..'z' then c.ord-'s'.ord+1
101       end
102     end
103     if ary.size > 1
104       return doTehArithmancy ary.reduce :+
105     else
106       #puts "returning #{ary[0]} (a #{ary[0].class})"
107       #puts "chr: #{ary[0].instance_variable_get(:@character)}"
108       return ary[0]
109     end
110   end
113 module Characterizable
114   def character= (*params)
115     @character = Character.new unless @character
116     @character.traits = params[0]
117     #puts "set character for #{self.to_s} to #{@character} according to #{params[0]}"
118   end
120   def character ()
121     return @character if @character
122     #puts "generating character for #{self.to_s}"
123     ch = Character.generate self.to_s
124     @character = ch unless frozen?
125     ch
126   end
129 class Fixnum
130   include Characterizable 
131   #                              ethic ,                    moral ,    tau    , i,agility,strength,stamina,expertise
132   0.character=Character::Ethic::NEUTRAL, Character::Moral::CHAOTIC, 2*Math::PI, 0,      0,       1,      1,        0
133   1.character=Character::Ethic::NEUTRAL, Character::Moral::LAWFUL , 2*Math::PI, 0,      0,       1,      1,        0
134   2.character=Character::Ethic::NEUTRAL, Character::Moral::NEUTRAL, 2*Math::PI, 0,      0,       1,      1,        0
135   3.character=Character::Ethic::GOOD   , Character::Moral::LAWFUL , 2*Math::PI, 0,      0,       1,      1,        0
136   4.character=Character::Ethic::NEUTRAL, Character::Moral::LAWFUL , 2*Math::PI, 0,      0,       1,      1,        0
137   5.character=Character::Ethic::GOOD   , Character::Moral::CHAOTIC, 2*Math::PI, 0,      0,       1,      1,        0
138   6.character=Character::Ethic::GOOD   , Character::Moral::LAWFUL , 2*Math::PI, 0,      0,       1,      1,        0
139   7.character=Character::Ethic::GOOD   , Character::Moral::NEUTRAL, 2*Math::PI, 0,      0,       1,      1,        0
140   8.character=Character::Ethic::NEUTRAL, Character::Moral::LAWFUL , 2*Math::PI, 0,      0,       1,      1,        0
141   9.character=Character::Ethic::NEUTRAL, Character::Moral::LAWFUL , 2*Math::PI, 0,      0,       1,      1,        0
144 class Symbol
145   include Characterizable 
146   all_symbols.each do |sym|
147     sym.character
148   end
151 class Method
152   def character ()
153     name.to_sym.character
154   end
157 class BasicObject
158   def character ()
159     self.class.name.to_sym.character
160   end
162   def process_call(method, name, *args, &block)
163     rv = method.bind(self).call(*args, &block)
164     if @@armed
165       @@armed = false
166       #puts "processing call: #{method} which is #{method.character || "uncharacteristic"} on #{self} which is a #{self.class} with #{character || "no character"} given #{args.size > 0?args:"no args"} and #{block || "no block"}"
168       character.step!
169       rv.character.step!
170       name.character.step!
171       args.each do |a| #FIXME constants ahead!
172         a.character.step!
173         a.character.acc! name.character, 0.01
174         a.character.acc! character, 0.01
175         character.acc! a.character, 0.001
176         rv.character.acc! a.character, 0.001
177         name.character.acc! a.character, 0.001
178       end
179       rv.character.acc! name.character, 0.01
180       rv.character.acc! character, 0.002
181       character.acc! name.character, 0.001
183       @@armed = true
184     end
185     return rv
186   end
188   def self.infect_method(name)
189     @@armed = false if @@armed
190     raw_method = self.instance_method(name.to_sym)
191     define_method name do |*args, &block|
192       self.process_call(raw_method, name, *args, &block)
193     end
194     @@armed = true if not @@armed.nil?
195   end
196   
197   def self.infect_all!
198     self.instance_methods.each do |m|
199       unless [:process_call, :bind, :call].include? m
200         #puts "\t-infecting #{m}"
201         infect_method(m)
202       end
203     end
204   end
206   def self.method_added(name)
207     infect_method(name) if @@armed
208   end
210   def self.armed?
211     @@armed
212   end
214   class_variable_set(:@@armed, nil)
216   def self.arm!
217     @@armed = true
218   end
220   def self.disarm!
221     class_variable_set(:@@armed, nil)
222   end
224   #infect global objects
225   ::Module.constants.each do |c|
226     unless [:Config].include? c
227       #puts "+infecting #{c}"
228       cs = ::Module.const_get(c)
229       cs.infect_all! if cs.is_a? ::Class
230     end
231   end
234 puts "Graf Zahl resurrected."
235 BasicObject.arm!